package ec.linde.modulos.proyectos;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.data.gantt.Task;
import org.jfree.data.gantt.TaskSeries;
import org.jfree.data.gantt.TaskSeriesCollection;
import org.jfree.data.time.SimpleTimePeriod;
import org.primefaces.component.datatable.DataTable;
import org.primefaces.event.SelectEvent;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;
import org.primefaces.model.StreamedContent;

import ec.linde.enumeraciones.Estado;
import ec.linde.modulos.proyectos.servicio.ServicioPlanOperativo;
import ec.linde.modulos.proyectos.servicio.ServicioRdo;
import ec.linde.modulos.recursos.FuncionesUtiles;
import ec.linde.persistencia.entidades.proyectos.CronogramaPlanOperativo;
import ec.linde.persistencia.entidades.proyectos.CronogramaRdo;
import ec.linde.persistencia.entidades.proyectos.DetallePlanOperativo;
import ec.linde.persistencia.entidades.proyectos.DetalleRdo;
import ec.linde.persistencia.entidades.proyectos.PlanOperativo;
import ec.linde.persistencia.entidades.proyectos.Rdo;

/**
 * TODO: Agregar descripcion
 * 
 * @FechaCreacion: 29/03/2013
 * @author: Alex Carrillo
 * @FechaModificacion: 29/03/2013
 * @author: Alex Carrillo
 * @version: 1.0
 */
@ManagedBean
@ViewScoped
public class RdoBean extends PageController {

	private static final long serialVersionUID = 1L;

	/**
	 * Servicios
	 */
	@EJB
	private ServicioRdo servicioRdo;
	@EJB
	private ServicioPlanOperativo servicioPlanOperativo;

	/**
	 * Atributos Forma
	 */
	private Rdo rdo;
	private DetalleRdo detalleRdoSeleccionado;
	private PlanOperativo planOperativo;
	private StreamedContent chartImage;
	private boolean indicadorCosto;

	/**
	 * Listados 1. Lista Lazy DataModel 2. Lista Detalles 3. Listas Combo
	 */
	private LazyDataModel<PlanOperativo> listaPlanOperativo;
	private List<DetalleRdo> listaDetalleRdo;
	/**
	 * UI
	 */
	private DataTable dtPlanOperativo;
	private DataTable dtDetalleRdo;
	private DataTable dtCronogramaRdo;

	/**
	 * PostContrutor
	 */
	@PostConstruct
	public void init() {

		listaPlanOperativo = new LazyDataModel<PlanOperativo>() {

			private static final long serialVersionUID = 1L;

			@Override
			public List<PlanOperativo> load(int startIndex, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters) {

				List<PlanOperativo> lista = new ArrayList<PlanOperativo>();

				if (filters == null) {
					filters = new HashMap<String, String>();
				}
				filters.put("estado", Estado.APROBADO.toString());

				boolean ordenar = sortOrder == SortOrder.ASCENDING ? true : false;
				lista = servicioPlanOperativo.obtenerListaPorPagina(startIndex, pageSize, sortField, ordenar, filters);

				listaPlanOperativo.setRowCount(servicioPlanOperativo.contarPorCriterio(filters));

				return lista;

			}
		};

	}

	/**
	 * Metodos
	 */

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.asinfo.as2.controller.PageControllerAS2#editar()
	 */
	public String editar() {
		return "";
	}

	/*
	 * (non-Javadoc)\
	 * 
	 * @see com.asinfo.as2.controller.PageControllerAS2#guardar()
	 */
	public String guardar() {

		String resultado = "";
		try {

			if (validar()) {
				servicioRdo.guardar(rdo);
				addInfoMessage("Registro Guardado Correctamente");
				setEditado(false);
				limpiar();
				if (indicadorCosto) {
					resultado = "rdoConCosto.xhtml?faces-redirect=true";
				} else {
					resultado = "rdoSinCosto.xhtml?faces-redirect=true";
				}
			} else {
				resultado = "";
			}
		} catch (Exception e) {
			addErrorMessage("Error al Guardar el registro");
		}
		return resultado;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.asinfo.as2.controller.PageControllerAS2#eliminar()
	 */
	public String eliminar() {
		return "";
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.asinfo.as2.controller.PageControllerAS2#limpiar()
	 */
	@Override
	public String limpiar() {
		rdo = null;
		return "";
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ec.linde.modulos.proyectos.PageController#cancelar()
	 */
	@Override
	public String cancelar() {
		setEditado(false);
		limpiar();
		if (indicadorCosto) {
			return "rdoConCosto.xhtml?faces-redirect=true";
		} else {
			return "rdoSinCosto.xhtml?faces-redirect=true";
		}
	}

	/**
	 * TODO: Agregar descripcion del metodo
	 * 
	 * @return
	 */
	public List<DetalleRdo> getListaDetalleRdo() {
		if (listaDetalleRdo == null) {
			listaDetalleRdo = new ArrayList<DetalleRdo>();
			for (DetalleRdo detalleRdo : rdo.getListaDetalleRdo()) {
				if (!detalleRdo.isEliminado()) {
					listaDetalleRdo.add(detalleRdo);
				}
			}
		}
		return listaDetalleRdo;
	}

	/**
	 * TODO: Agregar descripcion del metodo
	 * 
	 * @return
	 */
	public List<CronogramaRdo> getListaCronogramaRdo() {
		List<CronogramaRdo> lista = new ArrayList<CronogramaRdo>();
		if (detalleRdoSeleccionado != null) {
			for (CronogramaRdo cronogramaRdo : detalleRdoSeleccionado.getListaCronogramaRdo()) {
				if (!cronogramaRdo.isEliminado()) {
					lista.add(cronogramaRdo);
				}
			}
		}
		FuncionesUtiles.ordenaLista(lista, "fecha", true);
		return lista;
	}

	/**
	 * Carga el plan operativo ya sea desde el proyecto o desde el ultimo plan operativo
	 */
	public void cargarRdoDesdePlanOperativo() {

		if (planOperativo != null && this.rdo == null) {

			// Cargo el detalle del Plan Operativo Actual
			planOperativo = servicioPlanOperativo.cargarDetalle(planOperativo.getId());

			// Cargo el plan operativo
			HashMap<String, String> filters = new HashMap<String, String>();
			filters.put("planOperativo.idPlanOperativo", "" + planOperativo.getId());
			List<Rdo> lista = servicioRdo.obtenerListaCombo("", true, filters);

			// Si la lista del rdo no existe me crea un nuevo rdo
			if (lista.isEmpty()) {

				this.rdo = new Rdo();
				rdo.setPlanOperativo(planOperativo);
				rdo.setIndicadorCosto(indicadorCosto);
				rdo.setFecha(new Date());

				for (DetallePlanOperativo dpo : planOperativo.getListaDetallePlanOperativo()) {

					DetalleRdo drdo = new DetalleRdo();
					drdo.setRdo(rdo);
					drdo.setPartida(dpo.getPartida());
					drdo.setConcepto(dpo.getConcepto());
					drdo.setCostoUnitario(dpo.getCostoUnitario());
					drdo.setCantidad(dpo.getCantidad());
					drdo.setCostoTarea(dpo.getCostoTarea());
					drdo.setPeso(dpo.getPeso());
					drdo.setFechaInicio(dpo.getFechaInicio());
					drdo.setFechaFin(dpo.getFechaFin());
					drdo.setDiasTarea(dpo.getDiasTarea());

					for (CronogramaPlanOperativo cpo : dpo.getListaCronogramaPlanOperativo()) {
						CronogramaRdo crdo = new CronogramaRdo();
						crdo.setDetalleRdo(drdo);
						crdo.setFecha(cpo.getFecha());
						crdo.setNombreDia(cpo.getNombreDia());
						crdo.setDiaNoLaborable(cpo.isDiaNoLaborable());
						crdo.setAvance(cpo.getAvance());
						drdo.getListaCronogramaRdo().add(crdo);
					}

					rdo.getListaDetalleRdo().add(drdo);
				}

			} else {

				// Cargo el detalle del Rdo Actual y seto el plan operativo asl rdo
				rdo = servicioRdo.cargarDetalle(lista.get(0).getId());
				rdo.setPlanOperativo(planOperativo);
				rdo.setIndicadorCosto(indicadorCosto);
				rdo.setFecha(new Date());

				// Se llena el hasmap de los detalles del rdo actual y se eliminan
				HashMap<String, DetalleRdo> hmDRdo = new HashMap<String, DetalleRdo>();
				for (DetalleRdo detalleRdo : rdo.getListaDetalleRdo()) {
					hmDRdo.put(detalleRdo.getPartida().getPadeId() + "~" + detalleRdo.getConcepto().getConcId(), detalleRdo);
					detalleRdo.setEliminado(true);
				}

				// Se recorre el detalle del plan operativo para habilitar los registros que estan en el plan operativo y dar el orden al rdo del plan
				// operativo
				for (DetallePlanOperativo dpo : planOperativo.getListaDetallePlanOperativo()) {

					// Verifica si existe el detalle del rdo y si existe lo activa caso contrario lo crea
					DetalleRdo detalleRdo = hmDRdo.get(dpo.getPartida().getPadeId() + "~" + dpo.getConcepto().getConcId());
					if (detalleRdo != null) {
						detalleRdo.setEliminadoDetalleRdo(false);
					} else {
						detalleRdo = new DetalleRdo();
						rdo.getListaDetalleRdo().add(detalleRdo);
					}
					detalleRdo.setRdo(rdo);
					detalleRdo.setConcepto(dpo.getConcepto());
					detalleRdo.setCostoUnitario(dpo.getCostoUnitario());
					detalleRdo.setCantidad(dpo.getCantidad());
					detalleRdo.setCostoTarea(dpo.getCostoTarea());
					detalleRdo.setPeso(dpo.getPeso());
					detalleRdo.setFechaInicio(dpo.getFechaInicio());
					detalleRdo.setFechaFin(dpo.getFechaFin());
					detalleRdo.setDiasTarea(dpo.getDiasTarea());

					// Verifica si existe el cronograma para el rdo y si existe lo activa caso contrario lo crea
					for (CronogramaPlanOperativo cpo : dpo.getListaCronogramaPlanOperativo()) {

						CronogramaRdo cronogramaRdo = detalleRdo.getTmCronogramaRdoConDiasLaborables().get(cpo.getFecha());

						if (cronogramaRdo != null) {
							cronogramaRdo.setEliminado(false);
						} else {
							cronogramaRdo = new CronogramaRdo();
							// cronogramaRdo.setAvance(cronogramaRdo.getAvance());
							detalleRdo.getListaCronogramaRdo().add(cronogramaRdo);
						}
						cronogramaRdo.setDetalleRdo(detalleRdo);
						cronogramaRdo.setFecha(cpo.getFecha());
						cronogramaRdo.setNombreDia(cpo.getNombreDia());
						cronogramaRdo.setDiaNoLaborable(cpo.isDiaNoLaborable());

					}
				}

			}

			setEditado(true);
		}
	}

	/**
	 * Selecciona la fila del Detalle operativo
	 * 
	 * @param event
	 */
	public void seleccionarDetalleRdo(SelectEvent event) {
		detalleRdoSeleccionado = (DetalleRdo) event.getObject();
	}

	/**
	 * Selecciona la fila del Detalle operativo
	 */
	public void seleccionarDtDetalleRdo() {
		detalleRdoSeleccionado = (DetalleRdo) dtDetalleRdo.getRowData();
	}

	/**
	 * VAlida el plan opertaivo antes de guardar
	 * 
	 * @return
	 */
	private boolean validar() {
		boolean resultado = true;
		for (DetalleRdo detalleRdo : rdo.getListaDetalleRdo()) {
			if (!detalleRdo.isEliminado() && (detalleRdo.getCantidad().compareTo(detalleRdo.getTotalAvanceCronograma()) < 0)) {
				resultado = false;
				addInfoMessage("El avance del cronograma es mayor a la cantidad planificada para la tarea :"
						+ detalleRdo.getConcepto().getConcNombre());
			}
		}
		return resultado;
	}

	/**
	 * Metodos Eventos
	 */

	/**
	 * Metodos GET Y SET
	 */

	/**
	 * Get del atributo rdo
	 * 
	 * @return el valor del atributo rdo
	 */
	public Rdo getRdo() {
		return rdo;
	}

	/**
	 * Set del atributo rdo
	 * 
	 * @param proyecto
	 *            valor para asignar al atributo rdo
	 */
	public void setRdo(Rdo rdo) {
		this.rdo = rdo;
	}

	public PlanOperativo getPlanOperativo() {
		return planOperativo;
	}

	public void setPlanOperativo(PlanOperativo planOperativo) {
		this.planOperativo = planOperativo;
	}

	public DetalleRdo getDetalleRdoSeleccionado() {
		return detalleRdoSeleccionado;
	}

	public void setDetalleRdoSeleccionado(DetalleRdo detalleRdoSeleccionado) {
		this.detalleRdoSeleccionado = detalleRdoSeleccionado;
	}

	public LazyDataModel<PlanOperativo> getListaPlanOperativo() {
		return listaPlanOperativo;
	}

	public void setListaPlanOperativo(LazyDataModel<PlanOperativo> listaPlanOperativo) {
		this.listaPlanOperativo = listaPlanOperativo;
	}

	public DataTable getDtPlanOperativo() {
		return dtPlanOperativo;
	}

	public void setDtPlanOperativo(DataTable dtPlanOperativo) {
		this.dtPlanOperativo = dtPlanOperativo;
	}

	/**
	 * Get del atributo dtCronogramaRdo
	 * 
	 * @return el valor del atributo dtCronogramaRdo
	 */
	public DataTable getDtCronogramaRdo() {
		return dtCronogramaRdo;
	}

	/**
	 * Set del atributo dtCronogramaRdo
	 * 
	 * @param dtCronogramaRdo
	 *            valor para asignar al atributo dtCronogramaRdo
	 */
	public void setDtCronogramaRdo(DataTable dtCronogramaRdo) {
		this.dtCronogramaRdo = dtCronogramaRdo;
	}

	/**
	 * Get del atributo dtDetalleRdo
	 * 
	 * @return el valor del atributo dtDetalleRdo
	 */
	public DataTable getDtDetalleRdo() {
		return dtDetalleRdo;
	}

	/**
	 * Set del atributo dtDetalleRdo
	 * 
	 * @param dtDetalleRdo
	 *            valor para asignar al atributo dtDetalleRdo
	 */
	public void setDtDetalleRdo(DataTable dtDetalleRdo) {
		this.dtDetalleRdo = dtDetalleRdo;
	}

	public StreamedContent getChartImage() {
		return chartImage;
	}

	public void setChartImage(StreamedContent chartImage) {
		this.chartImage = chartImage;
	}

	public void actualizarCronograma() {

		try {

			String image = rdo.getPlanOperativo().getImagen();

			TaskSeries taskSeries = new TaskSeries("Avance");

			for (DetalleRdo detalleRdo : rdo.getListaDetalleRdo()) {
				if (!detalleRdo.isEliminado()) {
					taskSeries.add(new Task(detalleRdo.getConcepto().getConcNombre(), new SimpleTimePeriod(detalleRdo.getFechaInicio(), detalleRdo
							.getFechaFin())));
				}
			}

			TaskSeriesCollection collection = new TaskSeriesCollection();
			collection.add(taskSeries);

			JFreeChart jfreechart = ChartFactory.createGanttChart("Cronograma", "Tareas", "Dias", collection, true, true, false);
			File chartFile = new File("D:" + File.separator + image);
			ChartUtilities.saveChartAsPNG(chartFile, jfreechart, 1400, 400);
			chartImage = new DefaultStreamedContent(new FileInputStream(chartFile), "image/png");

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public boolean isIndicadorCosto() {
		return indicadorCosto;
	}

	public void setIndicadorCosto(boolean indicadorCosto) {
		this.indicadorCosto = indicadorCosto;
	}

}
